#include "g711encoder.h"
#include <QDebug>

const int16_t ALAW_MAX = 0xFFF;
const int16_t ALAW_BIAS = 0x84;
#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
#define	NSEGS		(8)		/* Number of A-law segments. */
#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
#define	SEG_MASK	(0x70)		/* Segment field mask. */
static int seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
                0x1FF, 0x3FF, 0x7FF, 0xFFF};
static int seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
                0x3FF, 0x7FF, 0xFFF, 0x1FFF};

uint8_t G711Encoder::encodeMuLaw(int16_t pcm_val) {
    const uint16_t BIAS = 0x84;
    const uint16_t CLIP = 32635;
    uint8_t sign, exponent, mantissa;
    uint16_t magnitude;

    sign = (pcm_val < 0) ? 0x80 : 0;
    if (sign != 0) pcm_val = -pcm_val;
    pcm_val = pcm_val > CLIP ? CLIP : pcm_val;
    pcm_val += BIAS;
    magnitude = (pcm_val > 0xFF) ? pcm_val >> 1 : pcm_val;

    exponent = 7;
    for (uint16_t exp_mask = 0x4000; (magnitude & exp_mask) == 0 && exponent > 0; exp_mask >>= 1) {
        exponent--;
    }

    mantissa = (magnitude >> (exponent + 3)) & 0x0F;
    return ~(sign | (exponent << 4) | mantissa);
}

uint8_t G711Encoder::encodeALaw(int16_t pcm_val) {
    const uint16_t BIAS = 0x84;
    const uint16_t CLIP = 32635;
    uint8_t sign, exponent, mantissa;
    uint16_t magnitude;

    sign = (pcm_val < 0) ? 0x00 : 0x80;
    if (sign != 0) pcm_val = -pcm_val;
    pcm_val = pcm_val > CLIP ? CLIP : pcm_val;
    pcm_val += BIAS;
    magnitude = (pcm_val > 0xFF) ? pcm_val >> 1 : pcm_val;

    exponent = 7;
    for (uint16_t exp_mask = 0x4000; (magnitude & exp_mask) == 0 && exponent > 0; exp_mask >>= 1) {
        exponent--;
    }

    mantissa = (magnitude >> (exponent + 3)) & 0x0F;
    return (sign | (exponent << 4) | mantissa) ^ 0x55;
}

uint8_t G711Encoder::encodeALaw1(int16_t pcm_val)
{

    qDebug()<<"原始的pcm_val： "<< pcm_val;
    const int ALAW_MAX = 0xFFF; // 定义 A-law 最大值
        int mask = 0xD5; // 定义掩码，初始为 0xD5
        int seg = 0; // 定义段位
        uint8_t aval = 0; // 定义 A-law 值

        // 如果输入的 PCM 值是负数
        if (pcm_val < 0) {
            pcm_val = -pcm_val - 1; // 取反，并减 1
            mask = 0x55; // 改变掩码为 0x55
        }
        if (pcm_val > ALAW_MAX) pcm_val = ALAW_MAX; // 限制 PCM 值在最大值内

        // 计算段位
        for (int i = 0; i < 8; i++) {
            if (pcm_val <= (0x0F << (i + 1))) {
                seg = i;
                break;
            }
        }
        if (seg >= 8) {
            aval = 0x7F ^ mask; // 如果段位大于等于 8，设置 A-law 值为 0x7F 并与掩码异或
        } else {
            aval = (seg << 4) | ((pcm_val >> (seg + 1)) & 0x0F); // 计算 A-law 值
            aval ^= mask; // 与掩码异或
        }
        qDebug()<<"编码后的aval： "<< aval;

        return aval; // 返回 A-law 编码后的值

}

uint8_t G711Encoder::encodeALaw2(int16_t pcm_val)
{
    int16_t mask;
    int16_t seg;
    uint8_t aval;
    //将线性PCM值右移3位，将其缩小到合理范围
    pcm_val = pcm_val >> 3;
    //如果值为正，mask为0xD5，否则为0x55，并将负值取反减1
    if (pcm_val >= 0) {
        mask = 0xD5;
    } else {
        mask = 0x55;
        pcm_val = -pcm_val - 1;
    }

    if (pcm_val > ALAW_MAX) pcm_val = ALAW_MAX;
    //加入偏移量ALAW_BIAS（0x84），这是A-law编码所需的常数
    pcm_val += ALAW_BIAS;

    seg = (pcm_val & 0x7000) >> 11;
    //通过位操作选择段位。
    if (seg >= 1) {
        aval = (uint8_t) (0x20 | seg << 4 | (pcm_val >> (seg + 3) & 0xF));
    } else {
        aval = (uint8_t) (pcm_val >> 4);
    }

    return (aval ^ mask);
}

int G711Encoder::search(int val, int *table, int size)
{
    int i;		/* changed from "short" *drago* */

    for (i = 0; i < size; i++) {
        if (val <= *table++)
            return (i);
    }
    return (size);
}

int G711Encoder::linear2alaw(int pcm_val)
{
    int		mask;	/* changed from "short" *drago* */
    int		seg;	/* changed from "short" *drago* */
    int		aval;

    pcm_val = pcm_val >> 3;//这里右移3位，因为采样值是16bit，而A-law是13bit，存储在高13位上，低3位被舍弃


    if (pcm_val >= 0) {
        mask = 0xD5;		/* sign (7th) bit = 1 二进制的11010101*/
    } else {
        mask = 0x55;		/* sign bit = 0  二进制的01010101*/
        pcm_val = -pcm_val - 1; //负数转换为正数计算
    }

    /* Convert the scaled magnitude to segment number. */
    seg = search(pcm_val, seg_aend, 8); //查找采样值对应哪一段折线

    /* Combine the sign, segment, and quantization bits. */

    if (seg >= 8)		/* out of range, return maximum value. */
        return (0x7F ^ mask);
    else {
//以下按照表格第一二列进行处理，低4位是数据，5~7位是指数，最高位是符号
        aval = seg << SEG_SHIFT;
        if (seg < 2)
            aval |= (pcm_val >> 1) & QUANT_MASK;
        else
            aval |= (pcm_val >> seg) & QUANT_MASK;
        return (aval ^ mask);
    }

}

int G711Encoder::alaw2linear(int a_val)
{
    int		t;      /* changed from "short" *drago* */
    int		seg;    /* changed from "short" *drago* */

    a_val ^= 0x55; //异或操作把mask还原

    t = (a_val & QUANT_MASK) << 4;//取低4位，即表中的abcd值，然后左移4位变成abcd0000
    seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; //取中间3位，指数部分
    switch (seg) {
    case 0: //表中第一行，abcd0000 -> abcd1000
        t += 8;
        break;
    case 1: //表中第二行，abcd0000 -> 1abcd1000
        t += 0x108;
        break;
    default://表中其他行，abcd0000 -> 1abcd1000 的基础上继续左移(按照表格第二三列进行处理)
        t += 0x108;
        t <<= seg - 1;
    }
    return ((a_val & SIGN_BIT) ? t : -t);

}

